<template>
  <!-- TransitionGroup组件: tag指定生成的dom元素, class样式等dom属性就相当于作用于根据tag生成的元素上, name代表内置动画名 -->
  <TransitionGroup name="list" class="list-container" tag="div">
    <div
      class="list-item"
      v-for="item in list"
      :key="item.id"
      :draggable="true"
      @dragstart="onDragstart($event, item)"
      @dragenter="onDragenter($event, item)"
      @dragend="onDragend"
      @dragover="onDragover"
    >
      {{ item.name }}
    </div>
  </TransitionGroup>
</template>

<script setup lang="ts">
import { ref } from "vue";

interface IListItem {
  name: string;
  id: number;
}

const list = ref<IListItem[]>([
  {
    name: "jrd",
    id: 1,
  },
  {
    name: "ywk",
    id: 2,
  },
  {
    name: "zwb",
    id: 3,
  },
]);

// TODO:
/**
 * 1. 拖拽事件总结
 * 2. dragover事件的默认行为是拖拽阴影回到原位。使用e.preventDefault来阻止默认行为
 */

let activeItem: IListItem | undefined;

const onDragenter = (_: DragEvent, item: IListItem) => {
  // console.log("enter", activeItem);
  swapItem(item);
  console.log(list.value[0]);
};

// 交换list数组中activeItem与targetItem
const swapItem = (targetItem: IListItem) => {
  const activeItemIndex = list.value.indexOf(activeItem!);
  const targetItemIndex = list.value.indexOf(targetItem);
  list.value.splice(activeItemIndex, 1, targetItem);
  list.value.splice(targetItemIndex, 1, activeItem!);
};

const onDragstart = (e: DragEvent, item: IListItem) => {
  // 借助了一个全局状态activeItem, 所以没有使用setData来传递数据(TODO)
  activeItem = item;
  // 滞后修改样式是因为: 拖拽的瞬间生成拖拽阴影(透明的原dom),生成阴影可见,所以不能把透明度置为完全透明.同时隐藏原位置的dom,修改其样式为完全透明
  setTimeout(() => {
    /**
     * ## 为什么要用 setTimeout(..., 0) 延迟？
     * 拖拽的瞬间，浏览器会自动生成一个“拖拽阴影”（即拖拽时跟随鼠标移动的那个半透明的 DOM 影像），这个影像是基于原始 DOM 元素的**快照**。
     * 如果你在 onDragstart 事件里立即把原始元素的透明度设为 0（比如加上 .moveing），那么浏览器生成的拖拽影像也会是透明的，看不到拖拽效果。
     * 用 setTimeout(..., 0) 把样式的更改延迟到事件循环的下一个 tick，这样浏览器已经生成了拖拽影像，再把原始元素隐藏，就不会影响拖拽影像的显示。
     */
    (e.target! as HTMLDivElement).classList.add("moveing"); // 通过dom.classList.(add / remove)的方式操作样式只能用e.target获取dom, e.currentTarget获取的dom无法访问classList属性
  }, 0);
};

// 拖拽结束
const onDragend = (e: DragEvent) => {
  (e.target! as HTMLDivElement).classList.remove("moveing");
};

const onDragover = (e: DragEvent) => {
  e.preventDefault(); // 默认行为: 拖拽阴影回到原位
};
</script>

<style scoped>
.list-container {
  width: 300px;
}
.list-container .list-item {
  height: 50px;
  margin-bottom: 10px;
  background-image: linear-gradient(
    0deg,
    rgba(100, 100, 100, 0.5),
    rgba(200, 200, 200, 0.5)
  );
}

/* list-move也是vue动画的内置样式，表示所属列表位置发生改变的元素 */
.list-move {
  transition: all 0.2s ease;
}

.moveing {
  opacity: 0;
}
</style>
